#include "ota_http.h"
#include <curl/curl.h>
#include "thirdparty/log.c/log.h"
#include "thirdparty/cJSON/cJSON.h"
#include "string.h"
#include <stdio.h>
#include <unistd.h>
#include <openssl/sha.h>

#define HTTP_DATA_BUFFER_LEN 1024
#define SHA_DATA_BUFFER_LEN 1024

static size_t write_data(char *ptr, size_t size, size_t nmemb, void *buf)
{
    int real_size = size * nmemb;
    if (real_size > HTTP_DATA_BUFFER_LEN - 1)
    {
        real_size = HTTP_DATA_BUFFER_LEN - 1;
    }
    memcpy(buf, ptr, real_size);
    memcpy(buf + real_size, "\0", 1);
    return real_size;
}

static size_t write_sha1(char *ptr, size_t size, size_t nmemb, void *buf)
{
    int real_size = size * nmemb;
    if (real_size > 2 * SHA_DIGEST_LENGTH)
    {
        real_size = 2 * SHA_DIGEST_LENGTH;
    }
    memcpy(buf, ptr, real_size);
    memcpy(buf + real_size, "\0", 1);
    return real_size;
}

static size_t write_file(char *ptr, size_t size, size_t nmemb, void *file)
{
    return fwrite(ptr, size, nmemb, (FILE *)file);
}

/**
 * @brief 内部函数，向指定http地址发送http请求
 *
 * @param url 地址
 * @param ptr 回调函数对应的指针
 * @param callback 收到数据的回调函数
 * @return int 0 成功，-1 失败
 */
static int ota_http_performHttpQuest(const char *url, void *ptr, curl_write_callback callback)
{
    CURL *curl = NULL;
    CURLcode result;
    int ret = 0;
    long code;

    curl = curl_easy_init();
    if (curl == NULL)
    {
        log_warn("Curl init failed");
        return -1;
    }

    // 设置CURL的属性
    curl_easy_setopt(curl, CURLOPT_URL, url);
    // 收到的数据处理
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
    // 跟随HTTP跳转
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);

    result = curl_easy_perform(curl);

    if (result != CURLE_OK)
    {
        // CURL请求出现问题
        log_warn("Curl perform failed: %s", curl_easy_strerror(result));
        ret = -1;
    }
    else
    {
        // 获取curl的返回值
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
        if (code != 200)
        {
            log_warn("Curl failed. Response code: %d", code);
            ret = -1;
        }
    }

    curl_easy_cleanup(curl);

    return ret;
}

static int ota_http_downloadFile(const char *url, const char *file_path)
{
    FILE *file = fopen(file_path, "wb");
    if (file == NULL)
    {
        log_warn("Open file failed!");
        return -1;
    }

    if (ota_http_performHttpQuest(url, file, write_file) < 0)
    {
        log_warn("Firmware url not valid");
        return -1;
    }

    return fclose(file);
}
int ota_http_getVersion(Version *version)
{
    char buf[HTTP_DATA_BUFFER_LEN];
    // 一般情况来说，服务器返回的Version数据为JSON格式
    log_info("Get version from server");
    int result = ota_http_performHttpQuest(VERSION_URL, buf, write_data);
    if (result < 0)
    {
        return -1;
    }

    // 将JSON数据转化为Version结构体
    cJSON *json_obj = cJSON_Parse(buf);
    if (!json_obj)
    {
        log_warn("Parse json failed");
        return -1;
    }
    cJSON *major = cJSON_GetObjectItem(json_obj, "major");
    cJSON *minor = cJSON_GetObjectItem(json_obj, "minor");
    cJSON *patch = cJSON_GetObjectItem(json_obj, "patch");
    if (!major || !minor || !patch)
    {
        log_warn("No valid version!");
        result = -1;
    }
    else
    {
        version->major = major->valueint;
        version->minor = minor->valueint;
        version->patch = patch->valueint;
        result = 0;
    }
    cJSON_Delete(json_obj);
    return result;
}

static int ota_http_calculateSHA1(char *firmware_filename, char *sha1)
{
    char dict[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    char buf[SHA_DATA_BUFFER_LEN];
    unsigned char sha_digest[SHA_DIGEST_LENGTH];
    FILE *file = fopen(firmware_filename, "rb");

    SHA_CTX sha_ctx;
    SHA1_Init(&sha_ctx);
    int len;
    while ((len = fread(buf, SHA_DATA_BUFFER_LEN, 1, file)))
    {
        SHA1_Update(&sha_ctx, buf, len);
    }
    SHA1_Final(sha_digest, &sha_ctx);

    for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
    {
        unsigned char temp = sha_digest[i];
        sha1[i * 2 + 1] = dict[temp & 0x0F];
        temp >>= 4;
        sha1[i * 2 + 0] = dict[temp & 0x0F];
    }
    sha1[SHA_DIGEST_LENGTH * 2] = 0;
    return 0;
}

int ota_http_getFirmware(char *firmware_filename)
{
    char remote_sha1[2 * SHA_DIGEST_LENGTH + 1];
    char local_sha1[2 * SHA_DIGEST_LENGTH + 1];
    // 请求二进制文件
    if (ota_http_downloadFile(FIRMWARE_URL, firmware_filename) < 0)
    {
        log_warn("Firmware download failed");
        return -1;
    }

    // 请求远端二进制文件的Hash
    if (ota_http_performHttpQuest(FIRMWARE_HASH_URL, remote_sha1, write_sha1) < 0)
    {
        log_warn("Firmware hash download failed");
        return -1;
    }

    // 计算本地二进制文件的Hash
    if (ota_http_calculateSHA1(local_sha1, firmware_filename) < 0)
    {
        log_warn("Firmware hash calculation failed");
        return -1;
    }

    // 比较两个Hash值
    if (memcmp(remote_sha1, local_sha1, 2 * SHA_DIGEST_LENGTH) != 0)
    {
        log_warn("Firmware hash mismatch");
        unlink(firmware_filename);
        return -1;
    }

    return 0;
}
